package org.third.integration.ssh;

import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelExec;
import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.channel.ClientChannelEvent;
import org.apache.sshd.client.future.AuthFuture;
import org.apache.sshd.client.future.ConnectFuture;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.client.subsystem.sftp.SftpClient;
import org.apache.sshd.client.subsystem.sftp.SftpClient.DirEntry;
import org.apache.sshd.client.subsystem.sftp.SftpClientFactory;
import org.apache.sshd.common.future.WaitableFuture;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.command.Command;
import org.apache.sshd.server.command.CommandFactory;
import org.apache.sshd.server.config.keys.DefaultAuthorizedKeysAuthenticator;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.scp.ScpCommandFactory;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.ProcessShellFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;

public class SshdCliTest {
	private final static Logger logger = LoggerFactory.getLogger(SshdCliTest.class);

	public static void main(String[] args) {
		SshdCliTest tester = new SshdCliTest();
		String hostname = "dynasecurw1.hpeswlab.net";
		String user = "root";
		String password = "iso*help";
		String command = "date; hostname -f; date;dax";

		try {
			// tester.executeCommandOnRemote(hostname, user, password, command);
			 tester.executeCommandOnRemoteShell(hostname, user, password, command);

//			tester.sshdClientSftpTest(hostname, user, password);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private void executeCommandOnRemoteShell(String hostname, String username, String password, String cmd) {
		try {
			org.apache.sshd.client.SshClient client = SshClient.setUpDefaultClient();
			client.start();
			org.apache.sshd.client.future.ConnectFuture connectFuture = client.connect(username, hostname, 22);
			connectFuture.await();
			ClientSession sshSession = connectFuture.getSession();
			sshSession.addPasswordIdentity(password);
			// session.addPublicKeyIdentity(SecurityUtils.loadKeyPairIdentity("keyname", new
			// FileInputStream("priKey.pem"), null));
			WaitableFuture authFuture = sshSession.auth();
			authFuture.await();
			if (!authFuture.isDone())
				System.out.println("auth failed");

			ChannelShell channelShell = sshSession.createShellChannel();
			channelShell.setOut(System.out);
			channelShell.open();

			InputStream userInputStream = System.in;
			channelShell.setIn(userInputStream);
			Collection<ClientChannelEvent> mask = new ArrayList<ClientChannelEvent>();
			mask.add(ClientChannelEvent.EXIT_SIGNAL);
			channelShell.waitFor(mask, 0);
			
			userInputStream.close();
			System.out.println("close");
			channelShell.close();

			client.stop();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	private void executeCommandOnRemoteShell2(String hostname, String username, String password, String cmd) {
		try {

			// Map<PtyMode, Integer> tty = SttySupport.parsePtyModes(PtyMode.TTY_OP_OSPEED);
			SshClient client = SshClient.setUpDefaultClient();
			client.start();
			ConnectFuture connectFuture = client.connect(username, hostname, 22);
			connectFuture.await();
			ClientSession sshSession = connectFuture.getSession();
			sshSession.addPasswordIdentity(password);
			// session.addPublicKeyIdentity(SecurityUtils.loadKeyPairIdentity("keyname", new
			// FileInputStream("priKey.pem"), null));
			WaitableFuture authFuture = sshSession.auth();
			authFuture.await();
			if (!authFuture.isDone())
				System.out.println("auth failed");

			ChannelShell channel = (ChannelShell) sshSession.createShellChannel();
			// channel.setPtyModes(tty);
			PipedOutputStream out = new PipedOutputStream();
			PipedInputStream channelIn = new PipedInputStream(out);
			PipedOutputStream channelOut = new PipedOutputStream();
			PipedInputStream in = new PipedInputStream(channelOut);
			channel.setIn(channelIn);
			channel.setOut(channelOut);
			channel.setErr(new ByteArrayOutputStream());
			channel.open();

			client.stop();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public static String executeCommandOnRemoteExec(String hostname, String username, String password, String cmd) {
		try {
			org.apache.sshd.client.SshClient client = SshClient.setUpDefaultClient();
			client.start();
			ConnectFuture connectFuture = client.connect(username, hostname, 22);
			connectFuture.await();
			ClientSession sshSession = connectFuture.getSession();
			sshSession.addPasswordIdentity(password);
			// session.addPublicKeyIdentity(SecurityUtils.loadKeyPairIdentity("keyname", new
			// FileInputStream("priKey.pem"), null));
			WaitableFuture authFuture = sshSession.auth();
			authFuture.await();
			if (!authFuture.isDone())
				System.out.println("auth failed");

			ChannelExec ec = sshSession.createExecChannel(cmd);
			ec.setOut(System.out);
			ec.open();
			Collection<ClientChannelEvent> mask = new ArrayList<ClientChannelEvent>();
			ec.waitFor(mask, 0);
			ec.close();

			client.stop();

		} catch (Exception e) {
			e.printStackTrace();
		}

		return null;
	}

	public void sshdClientSftpTest(String hostname, String user, String password)
			throws IOException, InterruptedException {
		Path src = Paths.get("src_sshd.txt");
		Files.deleteIfExists(src);
		Files.createFile(src);
		Files.write(src, "adsfa\nsdfs".getBytes());

		SshClient client = SshClient.setUpDefaultClient();
		client.start();
		ConnectFuture connectFuture = client.connect(user, hostname, 22);
		connectFuture.await();
		ClientSession session = connectFuture.getSession();

		session.addPasswordIdentity(password);
		// session.addPublicKeyIdentity(
		// SecurityUtils.loadKeyPairIdentity("keyname", new
		// FileInputStream("priKey.pem"), null));
		AuthFuture authFuture = session.auth();
		authFuture.await();
		if (!authFuture.isSuccess())
			System.out.println("auth failed");

		SftpClientFactory sftpClientFactory = SftpClientFactory.instance();
		SftpClient sftp = sftpClientFactory.createSftpClient(session);
		for (DirEntry de : sftp.readDir("."))
			System.out.println(de.getFilename() + " " + de.getAttributes().getType());

		System.out.println("Uploading the file: src_sshd.txt");
		OutputStream os = sftp.write("src_sshd.txt");
		Files.copy(src, os);
		os.close();

		// sftp.remove("delete_file.txt");

		InputStream is = sftp.read(".bash_profile");
		Path dst = Paths.get("dst1_sshd.bash_profile");
		Files.deleteIfExists(dst);
		System.out.println("Dowloading the file .bash_profile as dst1_ssh.bash_profile");
		Files.copy(is, dst);
		is.close();

		sftp.close();
		client.stop();
	}

	public void serverTest() throws IOException, InterruptedException {
		SshServer sshd = SshServer.setUpDefaultServer();
		sshd.setPort(22);

		// *give host key generator a path, when sshd server restart, the same key will
		// be load and used to authenticate the server
		sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(Paths.get("hostkey.ser")));

		sshd.setPasswordAuthenticator(new PasswordAuthenticator() {
			@Override
			public boolean authenticate(String username, String password, ServerSession session) {
				System.out.println("authen:  user=" + username + "  password=" + password);
				if ("bellring".equals(username) && "123456".equals(password))
					return true;
				return false;
			}
		});

		// use file ~/.ssh/authorized_keys
		sshd.setPublickeyAuthenticator(new DefaultAuthorizedKeysAuthenticator(false));

		// * CommandFactory can be userd in addition to the ShellFactory,
		// * it can also be used instead of the ShellFactory.
		// * The CommandFactory is used when direct commands are sent to the SSH server,
		// * as this is the case when running ssh localhost shutdown or scp xxx
		ScpCommandFactory scpCmdFactory = new ScpCommandFactory();
		scpCmdFactory.setDelegateCommandFactory(new CommandFactory() {
			public Command createCommand(String command) {
				System.out.println("command = \"" + command + "\"");
				return new ProcessShellFactory(("cmd /c " + command).split(" ")).create();
			}
		});
		sshd.setCommandFactory(scpCmdFactory);

		sshd.start();
	}
}
